Jdk_8 LocalDateTime整合SpringBoot序列化与反序列化

弃Date转LocalDateTime

以前用的是Date作为时间组件,Spring MVC会自动进行 Date<--->时间戳 的转换,然而新项目换成Jdk8新日期时间Api,就不会自动转换成时间戳了,如果强行接收时间戳给新日期时间Api则会直接报400。如果直接返回新日期时间Api则会返回类似下图的结果,这可能不是我们想要的,前端处理起来也没有时间戳来得方便。

未适配的返回结果

  • Java8全新日期时间API

    Instant 它代表的是时间戳
    LocalDate 不包含具体时间的日期,比如2014-01-14。它可以用来存储生日,周年纪念日,入职日期等。
    LocalTime 它代表的是不含日期的时间
    LocalDateTime 它包含了日期及时间,不过还是没有偏移信息或者说时区。
    ZonedDateTime 这是一个包含时区的完整的日期时间,偏移量是以UTC/格林威治时间为基准的。

如果我们要让框架支持新时间,我们可以自己写序列化与反序列化。

pom引入jar包。

<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
<version>2.9.9</version>
</dependency>

定义LocalDateTime序列化组件。

package vc.thinker.userservice.common;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;
import java.time.*;

/**
* LocalDateTime 序列化成时间戳
*
* @author HeTongHao
* @since 2019-07-15 18:23
*/
public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {

public static final LocalDateTimeSerializer INSTANCE;

static {
INSTANCE = new LocalDateTimeSerializer();
}

private LocalDateTimeSerializer() {
}

/**
* 当前时区偏移量
*/
private static final ZoneOffset CURRENT_ZONE_OFFSET = ZoneOffset.ofHours(8);

@Override
public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
//序列化为毫秒时间戳
jsonGenerator.writeNumber(localDateTime.toInstant(CURRENT_ZONE_OFFSET).toEpochMilli());
}
}

继承WebMvcConfigurer,重写configureMessageConverters方法,添加我们定义的序列化组件即可。

package vc.thinker.userservice.config;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.google.common.collect.Lists;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import vc.thinker.userservice.common.LocalDateTimeDeserializer;
import vc.thinker.userservice.common.LocalDateTimeSerializer;

import java.nio.charset.Charset;
import java.time.LocalDateTime;
import java.util.List;

/**
* WebMvc配置
*
* @author HeTongHao
* @since 2019-07-11 17:00
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
List<HttpMessageConverter<?>> myConverters = Lists.newArrayList();
// 添加 json 解析
MappingJackson2HttpMessageConverter jacksonConverter = new MappingJackson2HttpMessageConverter();
jacksonConverter.setPrettyPrint(true);

jacksonConverter.setObjectMapper(new ObjectMapper()
//支持jdk8的json转换(如转换LocalDateTime)
.registerModules(new JavaTimeModule()
.addSerializer(LocalDateTime.class, LocalDateTimeSerializer.INSTANCE)
.addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE)
)
//值为null的json字段忽略
.setSerializationInclusion(JsonInclude.Include.NON_NULL));

StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(Charset.forName("utf-8"));

/**
* 初步估计升级到 spring boot 2.0造成converters顺序有问题,会优使用框架定义的 converter,
* 这里调整自定义的在前
*/
myConverters.add(stringHttpMessageConverter);
myConverters.add(jacksonConverter);
myConverters.addAll(converters);
converters.clear();
converters.addAll(myConverters);
}
}

配置之后效果

配置之后效果

反序列化

以上示例只是说明了怎么去定义并且添加转换组件到spring中,以及怎么将返回数据类型为LocalDateTime的情况自动转换为时间戳。然而我们还需要让转换器支持从前端接收的时间戳。

虽然已经用new JavaTimeModule() 就可以自动识别接收的时间戳了,但是前端有可能是传一个字符串类型的时间戳,所以我们还要再定义一个反序列化组件来适配,最后记得像序列化组件一样注册即可。

下面贴上我的反序列化:

package vc.thinker.userservice.common;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;

import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;

/**
* 文本形式的时间戳,反序列化成LocalDateTime
*
* @author HeTongHao
* @since 2019-07-15 18:23
*/
public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {

public static final LocalDateTimeDeserializer INSTANCE;

static {
INSTANCE = new LocalDateTimeDeserializer();
}

private LocalDateTimeDeserializer() {
}

/**
* 当前时区偏移量
*/
private static final ZoneOffset CURRENT_ZONE_OFFSET = ZoneOffset.ofHours(8);

@Override
public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
return Instant.ofEpochMilli(Long.valueOf(jsonParser.getText())).atZone(CURRENT_ZONE_OFFSET).toLocalDateTime();
}
}
  • LocalDate、LocalTime、ZonedDateTime也是类似的适配方式,可以按需适配。
文章作者: 何同昊
文章链接: http://hetonghao.cn/2019/07/Jdk_8 LocalDateTime整合SpringBoot序列化与反序列化/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 何同昊 Blog
支付宝超级火箭🚀
微信超级火箭🚀